home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2005 October / PCWOCT05.iso / Software / FromTheMag / XAMPP 1.4.14 / xampp-win32-1.4.14-installer.exe / xampp / php / pear / Auth / SASL / DigestMD5.php < prev    next >
PHP Script  |  2004-03-24  |  8KB  |  195 lines

  1. <?php
  2. // +-----------------------------------------------------------------------+ 
  3. // | Copyright (c) 2002-2003 Richard Heyes                                 | 
  4. // | All rights reserved.                                                  | 
  5. // |                                                                       | 
  6. // | Redistribution and use in source and binary forms, with or without    | 
  7. // | modification, are permitted provided that the following conditions    | 
  8. // | are met:                                                              | 
  9. // |                                                                       | 
  10. // | o Redistributions of source code must retain the above copyright      | 
  11. // |   notice, this list of conditions and the following disclaimer.       | 
  12. // | o Redistributions in binary form must reproduce the above copyright   | 
  13. // |   notice, this list of conditions and the following disclaimer in the | 
  14. // |   documentation and/or other materials provided with the distribution.| 
  15. // | o The names of the authors may not be used to endorse or promote      | 
  16. // |   products derived from this software without specific prior written  | 
  17. // |   permission.                                                         | 
  18. // |                                                                       | 
  19. // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   | 
  20. // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     | 
  21. // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 
  22. // | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  | 
  23. // | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 
  24. // | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      | 
  25. // | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
  26. // | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
  27. // | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   | 
  28. // | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
  29. // | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  | 
  30. // |                                                                       | 
  31. // +-----------------------------------------------------------------------+ 
  32. // | Author: Richard Heyes <richard@php.net>                               | 
  33. // +-----------------------------------------------------------------------+ 
  34. // 
  35. // $Id: DigestMD5.php,v 1.6 2003/02/21 16:07:17 mj Exp $
  36.  
  37. /**
  38. * Implmentation of DIGEST-MD5 SASL mechanism
  39. *
  40. * @author  Richard Heyes <richard@php.net>
  41. * @access  public
  42. * @version 1.0
  43. * @package Auth_SASL
  44. */
  45.  
  46. require_once('Auth/SASL/Common.php');
  47.  
  48. class Auth_SASL_DigestMD5 extends Auth_SASL_Common
  49. {
  50.     /**
  51.     * Provides the (main) client response for DIGEST-MD5
  52.     * requires a few extra parameters than the other
  53.     * mechanisms, which are unavoidable.
  54.     * 
  55.     * @param  string $authcid   Authentication id (username)
  56.     * @param  string $pass      Password
  57.     * @param  string $challenge The digest challenge sent by the server
  58.     * @param  string $hostname  The hostname of the machine you're connecting to
  59.     * @param  string $service   The servicename (eg. imap, pop, acap etc)
  60.     * @param  string $authzid   Authorization id (username to proxy as)
  61.     * @return string            The digest response (NOT base64 encoded)
  62.     * @access public
  63.     */
  64.     function getResponse($authcid, $pass, $challenge, $hostname, $service, $authzid = '')
  65.     {
  66.         $challenge = $this->_parseChallenge($challenge);
  67.         $authzid_string = '';
  68.         if ($authzid != '') {
  69.             $authzid_string = ',authzid="' . $authzid . '"'; 
  70.         }
  71.  
  72.         if (!empty($challenge)) {
  73.             $cnonce         = $this->_getCnonce();
  74.             $digest_uri     = sprintf('%s/%s', $service, $hostname);
  75.             $response_value = $this->_getResponseValue($authcid, $pass, $challenge['realm'], $challenge['nonce'], $cnonce, $digest_uri, $authzid);
  76.  
  77.             return sprintf('username="%s",realm="%s"' . $authzid_string  . ',nonce="%s",cnonce="%s",nc="00000001",qop=auth,digest-uri="%s",response=%s,%d', $authcid, $challenge['realm'], $challenge['nonce'], $cnonce, $digest_uri, $response_value, $challenge['maxbuf']);
  78.         } else {
  79.             return PEAR::raiseError('Invalid digest challenge');
  80.         }
  81.     }
  82.     
  83.     /**
  84.     * Parses and verifies the digest challenge*
  85.     *
  86.     * @param  string $challenge The digest challenge
  87.     * @return array             The parsed challenge as an assoc
  88.     *                           array in the form "directive => value".
  89.     * @access private
  90.     */
  91.     function _parseChallenge($challenge)
  92.     {
  93.         $tokens = array();
  94.         while (preg_match('/^([a-z-]+)=("[^"]+(?<!\\\)"|[^,]+)/i', $challenge, $matches)) {
  95.  
  96.             // Ignore these as per rfc2831
  97.             if ($matches[1] == 'opaque' OR $matches[1] == 'domain') {
  98.                 $challenge = substr($challenge, strlen($matches[0]) + 1);
  99.                 continue;
  100.             }
  101.  
  102.             // Allowed multiple "realm" and "auth-param"
  103.             if (!empty($tokens[$matches[1]]) AND ($matches[1] == 'realm' OR $matches[1] == 'auth-param')) {
  104.                 if (is_array($tokens[$matches[1]])) {
  105.                     $tokens[$matches[1]][] = preg_replace('/^"(.*)"$/', '\\1', $matches[2]);
  106.                 } else {
  107.                     $tokens[$matches[1]] = array($tokens[$matches[1]], preg_replace('/^"(.*)"$/', '\\1', $matches[2]));
  108.                 }
  109.  
  110.             // Any other multiple instance = failure
  111.             } elseif (!empty($tokens[$matches[1]])) {
  112.                 $tokens = array();
  113.                 break;
  114.  
  115.             } else {
  116.                 $tokens[$matches[1]] = preg_replace('/^"(.*)"$/', '\\1', $matches[2]);
  117.             }
  118.  
  119.             // Remove the just parsed directive from the challenge
  120.             $challenge = substr($challenge, strlen($matches[0]) + 1);
  121.         }
  122.  
  123.         /**
  124.         * Defaults and required directives
  125.         */
  126.         // Realm
  127.         if (empty($tokens['realm'])) {
  128.             $uname = posix_uname();
  129.             $tokens['realm'] = $uname['nodename'];
  130.         }
  131.         
  132.         // Maxbuf
  133.         if (empty($tokens['maxbuf'])) {
  134.             $tokens['maxbuf'] = 65536;
  135.         }
  136.         
  137.         // Required: nonce, algorithm
  138.         if (empty($tokens['nonce']) OR empty($tokens['algorithm'])) {
  139.             return array();
  140.         }
  141.         
  142.         return $tokens;
  143.     }
  144.  
  145.     /**
  146.     * Creates the response= part of the digest response
  147.     *
  148.     * @param  string $authcid    Authentication id (username)
  149.     * @param  string $pass       Password
  150.     * @param  string $realm      Realm as provided by the server
  151.     * @param  string $nonce      Nonce as provided by the server
  152.     * @param  string $cnonce     Client nonce
  153.     * @param  string $digest_uri The digest-uri= value part of the response
  154.     * @param  string $authzid    Authorization id
  155.     * @return string             The response= part of the digest response
  156.     * @access private
  157.     */    
  158.     function _getResponseValue($authcid, $pass, $realm, $nonce, $cnonce, $digest_uri, $authzid = '')
  159.     {
  160.         if ($authzid == '') {
  161.             $A1 = sprintf('%s:%s:%s', pack('H32', md5(sprintf('%s:%s:%s', $authcid, $realm, $pass))), $nonce, $cnonce);
  162.         } else {
  163.             $A1 = sprintf('%s:%s:%s:%s', pack('H32', md5(sprintf('%s:%s:%s', $authcid, $realm, $pass))), $nonce, $cnonce, $authzid);
  164.         }
  165.         $A2 = 'AUTHENTICATE:' . $digest_uri;
  166.         return md5(sprintf('%s:%s:00000001:%s:auth:%s', md5($A1), $nonce, $cnonce, md5($A2)));
  167.     }
  168.  
  169.     /**
  170.     * Creates the client nonce for the response
  171.     *
  172.     * @return string  The cnonce value
  173.     * @access private
  174.     */
  175.     function _getCnonce()
  176.     {
  177.         if (file_exists('/dev/urandom')) {
  178.             return base64_encode(fread(fopen('/dev/urandom', 'r'), 32));
  179.  
  180.         } elseif (file_exists('/dev/random')) {
  181.             return base64_encode(fread(fopen('/dev/random', 'r'), 32));
  182.  
  183.         } else {
  184.             $str = '';
  185.             mt_srand((double)microtime()*10000000);
  186.             for ($i=0; $i<32; $i++) {
  187.                 $str .= chr(mt_rand(0, 255));
  188.             }
  189.             
  190.             return base64_encode($str);
  191.         }
  192.     }
  193. }
  194. ?>
  195.